package linker

import (
	"debug/elf"
	"fmt"
	"risc-v/ld/pkg/utils"
)

// InputFile  ELF 文件的结构
type InputFile struct {
	File                  *File
	SectionHeaders        []SectionHeader
	ElfSymbols            []ELFSymbol
	FirstGlobalSymbol     int
	SectionHeaderStrTable []byte
	SymbolStrTable        []byte
	IsAlive               bool      // 标记这个Object文件还是否需要 默认：静态链接库中的 ObjectFile 为 false; *.o文件为 true
	Symbols               []*Symbol // 这个 InputFile 中所有的符号
	LocalSymbols          []Symbol  // 保存本地符号，比如一个硬编码的字符串，相对应的 Global 类型的 符号就是会被外面用到的符号，比如一个全局变量
}

// NewInputFile 创建新的 ELF 文件实例
// 读取输入的 ObjectFile 中的头部信息(ELF 文件头 section header table 表中的每一项)
func NewInputFile(file *File) InputFile {
	f := InputFile{File: file}
	if len(file.Content) < int(FileHeaderSize) {
		utils.Fatal("File too small")
	}

	if !CheckMagic(file.Content) {
		utils.Fatal("not an ELF file")
	}

	// 读取文件头
	elfHeader := utils.Read[FileHeader](file.Content)

	// 读取 section header table 中第一个 section
	contents := file.Content[elfHeader.Shoff:]
	section := utils.Read[SectionHeader](contents)
	sectionNum := uint64(elfHeader.ShNum)

	// 如果 section 的数量超过一定的值，真实sectionNum为节头部表的第一个条目的 size
	//more details: https://www.sco.com/developers/gabi/latest/ch4.eheader.html
	if sectionNum == 0 {
		sectionNum = section.Size
	}

	f.SectionHeaders = []SectionHeader{section}
	for sectionNum > 1 {
		contents = contents[SectionHeaderSize:]
		f.SectionHeaders = append(f.SectionHeaders, utils.Read[SectionHeader](contents))
		sectionNum--
	}

	shStrTabIndex := int64(elfHeader.ShStrIndex)
	if elfHeader.ShStrIndex == uint16(elf.SHN_XINDEX) {
		shStrTabIndex = int64(section.Link)
	}

	f.SectionHeaderStrTable = f.GetBytesFromSectionIndex(shStrTabIndex)
	return f
}

// GetBytesFromSectionHeader 根据 SectionHeader 中的offset和size获取对应的节的字节流
func (f *InputFile) GetBytesFromSectionHeader(shr *SectionHeader) []byte {
	end := shr.Offset + shr.Size
	if end > uint64(len(f.File.Content)) {
		utils.Fatal(fmt.Sprintf("section header out of range %d", shr.Offset))
	}

	return f.File.Content[shr.Offset:end]
}

// GetBytesFromSectionIndex 通过节头部表中的下标来获取对应节的字节流
func (f *InputFile) GetBytesFromSectionIndex(index int64) []byte {
	return f.GetBytesFromSectionHeader(&f.SectionHeaders[index])
}

// FillUpELFSymbol 填充 InputFile.ElfSymbols 需要根据参数提供的 .symtab 的sectionHeader
func (f *InputFile) FillUpELFSymbol(s *SectionHeader) {
	byteStream := f.GetBytesFromSectionHeader(s)
	f.ElfSymbols = utils.ReadSlice[ELFSymbol](byteStream, SymbolEntrySize)
}

// FindSection 根据类型从 sectionHeaderTable 中找到对应 type 的 sectionHeader
// 如果没有找到 则返回 nil
func (f *InputFile) FindSection(Type uint32) *SectionHeader {
	size := len(f.SectionHeaders)
	for i := 0; i < size; i++ {
		if f.SectionHeaders[i].Type == Type {
			return &f.SectionHeaders[i]
		}
	}

	return nil
}
func (f *InputFile) GetELFHeader() FileHeader {
	return utils.Read[FileHeader](f.File.Content)
}
